<?php
/**
 * JSON文件操作类
 *
 * @author wangaibo<wangaibo168@163.com>
 * @charset utf-8
 * @version $Id
 */

class JsonReader {

    /**
     * 文件路径
     * @var null
     */
    private $path = null;
    /**
     * 文件句柄
     * @var null
     */
    private $fileHandler = null;

    public function __construct(){

    }

    /**
     * 检查是否已经初始化
     * @throws Exception
     */
    public function checkRead(){
        if(!$this->path || !is_resource($this->fileHandler)){
            throw new Exception('请先执行read方法');
        }
    }

    /**
     * 初始化文件
     * @param $path
     * @throws Exception
     */
    public function read($path){
        $this->close();
        if(!$path){
            throw new Exception('文件路径不存在');
        }
        if(file_exists($path)){
            if(is_dir($path)){
                throw new Exception('文件路径是一个目录');
            }
        }
        // 执行转换,excel转json
        $this->path = $path;
        $path = $this->convert();
        $this->fileHandler = fopen($path, 'r');
        $this->path = $path;
    }

    /**
     * 读取记录
     * @return array|null
     * @throws Exception
     */
    public function nextRow(){
        try{
            $this->checkRead();
            if(feof($this->fileHandler)) return null;
            $line = fgets($this->fileHandler);
            if($line===false) return null;
            // 如果数据为空,则读取下一条
            if(!$line){
                return $this->getRow();
            }
            // 解析
            $data = @json_decode($line, true);
            // 如果数据无效,则读取下一条
            if(!$data){
                return $this->getRow();
            }
            // 处理数据,移除前导单引号
            foreach($data as $k=>$v){
                $v = trim($v);
                if($v && substr($v, 0, 1)=='\''){
                    $v = substr($v,1);
                }
                $data[$k] = $v;
            }
            return $data;
        }catch(Exception $e){
            return null;
        }
    }

    /**
     * 关闭
     */
    public function close(){
        if(is_resource($this->fileHandler)){
            fclose($this->fileHandler);
        }
        $this->fileHandler = null;
    }

    /**
     * 转换,返回结果文件地址
     * @return string
     * @throws Exception
     */
    public function convert(){
        if(!$this->path || !file_exists($this->path) || !is_file($this->path)){
            throw new Exception('待转换文件不存在');
        }
        $this->close();
        $cmd = sprintf('%s/x2j.exe -p "%s"', dirname(__FILE__), $this->path);
        ob_start();
        $ret = system($cmd);
        ob_clean();
        if(!$ret || stripos($ret, 'success')===false){
            $this->path = null;
            throw new Exception('命令执行失败');
        }
        if(preg_match_all('/\\{(.+)\\}/', $ret, $matches)){
            $this->path = null;
            return $matches[1][0];
        }
        $this->path = null;
        throw new Exception('转换执行成功,但是没有发现结果文件');
    }

}

class JsonWriter {

    /**
     * 文件路径
     * @var null
     */
    private $path = null;
    /**
     * 文件句柄
     * @var null
     */
    private $fileHandler = null;

    public function __construct(){

    }

    /**
     * 检查是否已经初始化
     * @throws Exception
     */
    public function checkCreate(){
        if(!$this->path || !is_resource($this->fileHandler)){
            throw new Exception('请先执行create方法');
        }
    }

    /**
     * 初始化文件
     * @param $path
     * @throws Exception
     */
    public function create($path){
        $this->close();
        if(!$path){
            throw new Exception('文件路径不存在');
        }
        if(file_exists($path)){
            if(is_dir($path)){
                throw new Exception('文件路径是一个目录');
            }
            if(!unlink($path)){
                throw new Exception('文件已经存在,但是删除失败');
            }
        }
        $this->fileHandler = fopen($path, 'w');
        $this->path = $path;
    }

    /**
     * 添加工作簿
     * @param $name
     * @param $column
     * @param array $desc
     * @param int $width
     * @param int $height
     * @throws Exception
     */
    public function addSheet($name, $column, $desc = array(), $width = 20, $height = 20){
        if(!$name){
            throw new Exception('工作簿名称不能为空');
        }
        if(!$column){
            throw new Exception('字段列表不能为空');
        }
        if(!is_array($desc)){
            throw new Exception('描述说明必须为空数组');
        }
        // 检查字段
        foreach($column as $v){
            if(!isset($v['field']) || !$v['field']){
                throw new Exception('字段必须设置');
            }
            if(!isset($v['name']) || !$v['name']){
                throw new Exception('字段名称必须设置');
            }
            if(!isset($v['edit'])){
                throw new Exception('字段是否可编辑必须设置');
            }
        }
        $width = intval($width);
        if($width<=0) $width = 20;
        $height = intval($height);
        if($height<=0) $height = 20;
        $row = array('type' => 'sheet', 'data' => array(
            'name' => $name,
            'width' => $width,
            'height' => $height,
            'description' => $desc,
            'column' => $column
        ));
        if(fwrite($this->fileHandler, sprintf("%s\n", json_encode($row)))===false){
            throw new Exception('数据写入失败');
        }
        if(fflush($this->fileHandler)===false){
            throw new Exception('数据缓存刷新失败');
        }
    }

    /**
     * 添加记录
     * @param $data
     * @throws Exception
     */
    public function addRow($data){
        if(!$data){
            throw new Exception('数据无效');
        }
        $this->checkCreate();
        // 处理数据,加入前导单引号
        foreach($data as $k=>$v){
            $data[$k] = sprintf('\'%s', $v);
        }
        $row = array('type' => 'row', 'data' => $data);
        if(fwrite($this->fileHandler, sprintf("%s\n", json_encode($row)))===false){
            throw new Exception('数据写入失败');
        }
        if(fflush($this->fileHandler)===false){
            throw new Exception('数据缓存刷新失败');
        }
    }

    /**
     * 关闭
     */
    public function close(){
        if(is_resource($this->fileHandler)){
            fclose($this->fileHandler);
        }
        $this->fileHandler = null;
    }

    /**
     * 转换,返回结果文件地址
     * @return string
     * @throws Exception
     */
    public function convert(){
        if(!$this->path || !file_exists($this->path) || !is_file($this->path)){
            throw new Exception('待转换文件不存在');
        }
        $this->close();
        $cmd = sprintf('%s/j2x.exe -p "%s"', dirname(__FILE__), $this->path);
        ob_start();
        $ret = system($cmd);
        ob_clean();
        if(!$ret || stripos($ret, 'success')===false){
            // 移除文件
            @unlink(realpath($this->path));
            $this->path = null;
            throw new Exception('命令执行失败');
        }
        if(preg_match_all('/\\{(.+)\\}/', $ret, $matches)){
            @unlink(realpath($this->path));
            $this->path = null;
            return $matches[1][0];
        }
        // 移除文件
        @unlink(realpath($this->path));
        $this->path = null;
        throw new Exception('转换执行成功,但是没有发现结果文件');
    }

}